#include "server_helper.h"
#include "card_table.h"
using namespace dymy::config;
namespace dymy
{
    UserManager::UserManager() : m_users(), db_(std::make_unique<DBManager>())
    {
    }

    UserManager::UserSPtr UserManager::login(const std::string &user_name)
    {
        auto id = db_->query_user(user_name);
        if (id <= 0)
        {
            id = db_->insert_user(user_name);
        }

        auto it = m_users.find(id);
        if (it != m_users.end())
        {
            return it->second;
        }
        else
        {
            User *u = new User();
            u->id = id;
            u->user_name = user_name;
            UserSPtr spu(u);
            m_users.insert(online_users::value_type(id, spu));
            return spu;
        }
    }

    UserManager::UserSPtr UserManager::get_user(ID_t id)
    {
        UserManager::online_users::iterator it = m_users.find(id);

        if (it == m_users.end())
        {
            return nullptr;
        }
        else
        {
            return it->second;
        }
    }
    UserManager::online_users UserManager::get_users() const
    {
        return m_users;
    }
    void UserManager::logout(ID_t id)
    {
        m_users.erase(id);
    }

    class TableManager::TableManagerImpl
    {
        typedef table::TableController<aN, pN, eN, hN> TableController;
        typedef typename TableController::CardGenerator_ CardGenerator;

        TableController table_controller_;
        std::array<CardTableUPtr,tN> m_tables;
        inline CardTableUPtr &find_table(ID_t table_id)
        {
            return m_tables[table_id - 1];
        }
        inline int check_valid_table_user(ID_t table_id, size_t table_index, std::shared_ptr<User> &u) noexcept
        {
            if (!u)
            {
                return -2000;
            }

            if (table_id <= 0 || table_id > tN)
                return -2001;

            if (table_index < 0 || table_index >= pN)
            {
                return -2002;
            }
            return 0;
        }

    public:
        TableManagerImpl() : table_controller_(std::make_shared<CardGenerator>(std::make_shared<CardGenerator::AllCards>(card::MakeAllCards()))) {}
        int join_or_quit_table(std::shared_ptr<User> &u, ID_t table_id, size_t table_index, bool join)
        {
            int check_result = check_valid_table_user(table_id, table_index, u);
            if (check_result != 0)
            {
                return check_result;
            }

            auto &t = find_table(table_id);
            if (!join)
            {

                //1添加退出成功
                //0该位置没有这个人
                if (!t)
                {
                    return 0;
                }
                else
                {
                    if (table_controller_.remove_person(t.get(), table_index, u->id))
                    {
                        if (!t->has_person())
                        {
                            //如果没人，就删除该桌子
                            t.reset(nullptr);
                        }
                        else
                        {
                            //如果还有人，就停止游戏
                            table_controller_.stop(t.get());
                        }

                        u->last_online_table_id = 0;
                        return 1;
                    }

                    return -2006;
                }
            }
            else
            {
                if (u->last_online_table_id > 0 && (u->last_online_table_id != table_id || (t && t->find_user_index_by_user_id(u->id) >= 0)))
                {
                    return -2008;
                }

                if (!t)
                {
                    t.reset(new CardTable(table_id));
                }

                int r = table_controller_.add_person(t.get(), table_index, u->id, u->user_name);
                if (r < 0)
                    return r;
                //1添加用户成功
                //2该座位已经有人
                //0已经在这个房间

                if (r == 2)
                {
                    return -2003;
                }
                else if (r == 0)
                {
                    return -2004;
                }
                else
                {
                    u->last_online_table_id = table_id;
                    return 1;
                }
            }
        }

        // int quit_table_user(ID_t user_id, ID_t &table_id_result, size_t &index_result) { return 1; };

        int prepared_table(std::shared_ptr<User> &u, ID_t table_id, size_t table_index, bool prepared)
        {
            int check_result = check_valid_table_user(table_id, table_index, u);
            if (check_result < 0)
            {
                return check_result;
            }

            auto &t = find_table(table_id);
            if (!t)
            {
                return -2001;
            }
            int r = table_controller_.prepared(t.get(), table_index, u->id, prepared);
            if (r < 0)
                return r;

            if (r == 2)
            {
                if (prepared)
                {
                    return -2005;
                }
                else
                {
                    return -2007;
                }
            }
            else
            {
                return r;
            }
        };

        int rob_landlord_table(std::shared_ptr<User> &u, ID_t table_id, size_t table_index, bool rob)
        {
            int check_result = check_valid_table_user(table_id, table_index, u);
            if (check_result < 0)
            {
                return check_result;
            }

            auto &t = find_table(table_id);
            if (!t)
            {
                return -2001;
            }

            int r = table_controller_.rob_landlord(t.get(), table_index, u->id, rob);
            return r;
        };

        int play_cards_table(std::shared_ptr<User> &u, ID_t table_id, size_t table_index, const std::vector<dymy::card_t> &cards)
        {
            int check_result = check_valid_table_user(table_id, table_index, u);
            if (check_result < 0)
            {
                return check_result;
            }

            auto &t = find_table(table_id);
            if (!t)
            {
                return -2001;
            }

            int r = table_controller_.play_cards(t.get(), table_index, u->id, cards);

            return r;
        };

        CardTablePtr get_table(ID_t table_id)
        {
            if (table_id <= 0 || table_id > tN)
                return nullptr;
            else
            {
                return find_table(table_id).get();
            }
        };

        ssize_t find_free_position(ID_t table_id) noexcept
        {
            CardTablePtr tp = get_table(table_id);
            if (tp)
            {
                return tp->find_free_position();
            }
            else
            {
                return -1;
            }
        }
        ssize_t stop_table_if_user_quit(ID_t table_id, ID_t user_id)
        {
            CardTablePtr t = get_table(table_id);
            if (t)
            {
                return table_controller_.stop_table_if_user_quit(t, user_id);
            }
            return -1;
        }

        ID_t find_table_user(ID_t table_id, size_t index) noexcept(false)
        {
            CardTableUPtr &t = find_table(table_id);
            auto &p = t->find_user(index);
            if (p)
            {
                return p->user_id;
            }
            return 0;
        }

        constexpr size_t get_person_num() noexcept
        {
            return pN;
        }

        bool try_start(ID_t table_id)
        {
            auto table = get_table(table_id);
            if (table && table_controller_.start(table) == 1)
            {
                return true;
            }
            return false;
        };

        constexpr size_t get_table_num() noexcept
        {
            return tN;
        }

        bool contains_user_in_table(ID_t table_id, ID_t user_id)
        {
            auto t = get_table(table_id);
            if (t)
            {
                return t->find_user_index_by_user_id(user_id) >= 0;
            }
            return false;
        }

        bool fill_table_info(nlohmann::json::object_t &j_data, ID_t table_id)
        {
            using nlohmann::json;

            json::array_t j_table_users;
            //card_table* t = m_table_manager->get_table(table_id);
            auto t = get_table(table_id);
            if (t)
            {
                for (size_t i = 0; i < get_person_num(); ++i)
                {
                    auto &p = t->find_user(i);
                    if (!p)
                    {
                        j_table_users.push_back(nullptr);
                    }
                    else
                    {
                        std::vector<card_t> person_cards(p->cards.cbegin(), p->cards.cend());

                        json j_table_user = {
                            {"user_id", p->user_id},
                            {"name", p->user_name},
                            {"role", p->role},
                            {
                                "cards",
                                person_cards,
                            },
                            {
                                "prepared",
                                p->prepared,
                            },
                            {
                                "played_cards",
                                p->played_cards,
                            },
                            {
                                "status",
                                p->status,
                            }

                        };
                        j_table_users.push_back(j_table_user);
                    }
                }

                j_data["id"] = t->get_table_id();
                j_data["users"] = j_table_users;
                j_data["landlord_index"] = t->get_landlord_index();
                j_data["winner_index"] = t->get_winner_index();
                auto &cards3 = t->get_cards3();
                j_data["play_cards_index"] = t->get_play_cards_index();
                j_data["prev_play_max_card_index"] = t->get_prev_play_max_card_index();

                std::vector<card_t> current_play_max_cards_result;
                using CardsClassifyType = compartor::CardsClassify::CardsClassifyType;
                CardsClassifyType current_play_max_cards_type = CardsClassifyType::TYPE_INVALID;
                auto &current_play_max_cards = t->get_current_play_max_cards();
                if (current_play_max_cards)
                {
                    current_play_max_cards_result = current_play_max_cards->collect_cards();
                    current_play_max_cards_type = current_play_max_cards->type;
                }

                j_data["current_play_max_cards"] = current_play_max_cards_result;
                j_data["current_play_max_cards_type"] = current_play_max_cards_type;

                //j_data["aa"] = t->get_current_play_max_cards
                //t->get_curr
                j_data["cards3"] = cards3;
                j_data["step"] = t->get_step();
                return true;
            }
            return false;
        }

        bool fill_server_info(nlohmann::json::array_t &j_data)
        {
            using nlohmann::json;
            for (size_t i = 0; i < get_table_num(); ++i)
            {
                ID_t table_id = i + 1;
                int status = 0;
                auto t = get_table(table_id);
                json::object_t j_table;
                json::array_t j_table_users;
                if (t)
                {
                    for (size_t i = 0; i < get_person_num(); ++i)
                    {
                        auto &p = t->find_user(i);
                        if (!p)
                        {
                            j_table_users.push_back(nullptr);
                        }
                        else
                        {
                            json j_table_user = {
                                {"user_id", p->user_id},
                                {"name", p->user_name}

                            };
                            j_table_users.push_back(j_table_user);
                        }
                    }
                    status = t->get_step();
                }
                else
                {
                    for (size_t i = 0; i < get_person_num(); ++i)
                    {
                        j_table_users.push_back(nullptr);
                    }
                    status = STEP_WATING;
                }
                j_table["id"] = table_id;
                j_table["status"] = status;
                j_table["users"] = j_table_users;
                j_data.push_back(j_table);
            }
            return true;
        }

        inline ssize_t find_user_index(ID_t table_id, ID_t user_id)
        {
            auto t = get_table(table_id);
            if (t)
            {
                return t->find_user_index_by_user_id(user_id);
            }
            return -1;
        }

        inline bool exist_table(ID_t table_id) noexcept
        {
            CardTablePtr tp = get_table(table_id);
            return tp;
        }

        inline const multiple_card_t &find_table_user_cards(ID_t table_id, size_t index) noexcept(false)
        {
            CardTableUPtr &t = find_table(table_id);
            auto &p = t->find_user(index);
            return p->cards;
        }

        const HoleCards &find_table_hole_cards(ID_t table_id) noexcept(false)
        {
            CardTableUPtr &t = find_table(table_id);
            return t->get_cards3();
        }
    };
    TableManager::TableManager() : impl_(std::make_unique<TableManagerImpl>())
    {
    }

    TableManager::TableManager(TableManager &&) = default;
    TableManager &TableManager::operator=(TableManager &&) = default;
    TableManager::~TableManager() = default;

    int TableManager::join_or_quit_table(std::shared_ptr<User> &u, ID_t table_id, size_t table_index, bool join)
    {
        return impl_->join_or_quit_table(u, table_id, table_index, join);
    };

    int TableManager::prepared_table(std::shared_ptr<User> &u, ID_t table_id, size_t table_index, bool prepared)
    {
        return impl_->prepared_table(u, table_id, table_index, prepared);
    };

    int TableManager::rob_landlord_table(std::shared_ptr<User> &u, ID_t table_id, size_t table_index, bool rob)
    {
        return impl_->rob_landlord_table(u, table_id, table_index, rob);
    };

    int TableManager::play_cards_table(std::shared_ptr<User> &u, ID_t table_id, size_t table_index, const std::vector<dymy::card_t> &cards)
    {
        return impl_->play_cards_table(u, table_id, table_index, cards);
    };

    TableManager::CardTablePtr TableManager::get_table(ID_t table_id)
    {
        return impl_->get_table(table_id);
    };

    ssize_t TableManager::find_free_position(ID_t table_id) noexcept
    {
        return impl_->find_free_position(table_id);
    };

    ssize_t TableManager::stop_table_if_user_quit(ID_t table_id, ID_t user_id)
    {
        return impl_->stop_table_if_user_quit(table_id, user_id);
    }
    ID_t TableManager::find_table_user(ID_t table_id, size_t table_index) noexcept(false)
    {
        return impl_->find_table_user(table_id, table_index);
    }
    size_t TableManager::get_person_num() noexcept
    {
        return impl_->get_person_num();
    }

    bool TableManager::try_start(ID_t table_id)
    {
        return impl_->try_start(table_id);
    }

    size_t TableManager::get_table_num() noexcept
    {
        return impl_->get_table_num();
    }

    bool TableManager::contains_user_in_table(ID_t table_id, ID_t user_id) noexcept
    {
        return impl_->contains_user_in_table(table_id, user_id);
    }
    bool TableManager::fill_table_info(nlohmann::json::object_t &j_data, ID_t table_id)
    {
        return impl_->fill_table_info(j_data, table_id);
    }

    bool TableManager::fill_server_info(nlohmann::json::array_t &j_data)
    {
        return impl_->fill_server_info(j_data);
    }

    ssize_t TableManager::find_user_index(ID_t table_id, ID_t user_id) noexcept
    {
        return impl_->find_user_index(table_id, user_id);
    }

    bool TableManager::exist_table(ID_t table_id) noexcept
    {
        return impl_->exist_table(table_id);
    }

    const multiple_card_t &TableManager::find_table_user_cards(ID_t table_id, size_t index) noexcept(false)
    {
        return impl_->find_table_user_cards(table_id, index);
    }

    const TableManager::HoleCards &TableManager::find_table_hole_cards(ID_t table_id) noexcept(false)
    {
        return impl_->find_table_hole_cards(table_id);
    }
}